/**
 * Copyright 2006 Brad Reynolds
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.leeloo.databinding.internal.ui;

import java.util.Arrays;
import java.util.List;

import org.eclipse.core.runtime.ListenerList;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.leeloo.databinding.ui.view.EventLog;

/**
 * Provides notifications of events that the {@link EventLog} is interested in.
 * 
 * @author Brad Reynolds
 */
public class DebugLifeCycle implements IDebugEventSetListener {
    private static final int START = 0;

    private static final int STOP = 1;

    private static final int SUSPEND = 2;

    private static final int RESUME = 3;

    private ListenerList cycleListeners;

    /**
     * The breakpoints to not report suspend events for.
     */
    private List ignoreBreakpoints;

    private volatile boolean suspended;

    public DebugLifeCycle() {
    }
    
    /**
     * Breakpoints to ignore when evaluating suspend events.
     * 
     * @param breakpoints
     */
    public void setIgnoreBreakpoints(IBreakpoint[] breakpoints) {
        this.ignoreBreakpoints = (breakpoints != null) ? Arrays.asList(breakpoints) : null;
    }

    /**
     * @return <code>true</code> when suspended
     */
    public boolean isSuspended() {
        return suspended;
    }

    public void handleDebugEvents(DebugEvent[] events) {
        DebugEvent event = null;
        if (events.length > 0) {
            event = events[0];
        } else {
            return;
        }

        int type = -1;

        switch (event.getKind()) {
            case DebugEvent.CREATE:
                if (event.getSource() instanceof IProcess) {
                    type = START;
                }
                break;
            case DebugEvent.TERMINATE:
                if (event.getSource() instanceof IProcess) {
                    type = STOP;
                }
                break;
            case DebugEvent.RESUME:
                // fall thru
            case DebugEvent.SUSPEND:

                if (event.getSource() instanceof IJavaThread) {
                    IJavaThread thread = (IJavaThread) event.getSource();

                    IBreakpoint[] currentBreakpoints = thread.getBreakpoints();
                    boolean skip = false;

                    if (currentBreakpoints != null && ignoreBreakpoints != null) {
                        for (int i = 0; currentBreakpoints != null && skip == false && i < currentBreakpoints.length; i++) {
                            IBreakpoint breakpoint = currentBreakpoints[i];

                            skip = ignoreBreakpoints.contains(breakpoint);
                        }
                    }

                    if (!skip && !suspended && event.getKind() == DebugEvent.SUSPEND && currentBreakpoints != null
                            && currentBreakpoints.length > 0) {
                        suspended = true;
                        type = SUSPEND;
                    } else if (suspended && event.getKind() == DebugEvent.RESUME && !event.isStepStart()) {
                        suspended = false;
                        type = RESUME;
                    }
                }
                break;
        }

        if (type != -1 && cycleListeners != null) {
            Object[] listeners = cycleListeners.getListeners();
            for (int i = 0; i < listeners.length; i++) {
                IDebugLifeCycleListener listener = (IDebugLifeCycleListener) listeners[i];
                switch (type) {
                    case START:
                        listener.startDebug(events);
                        break;
                    case STOP:
                        listener.stopDebug(events);
                        break;
                    case SUSPEND:
                        listener.suspended(events);
                        break;
                    case RESUME:
                        listener.resumed(events);
                        break;
                }
            }
        }
    }

    public void addDebugLifeCycleListener(IDebugLifeCycleListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("Parameter listener was null."); //$NON-NLS-1$
        }

        if (cycleListeners == null) {
            cycleListeners = new ListenerList();
        }

        cycleListeners.add(listener);
    }

    public void removeDebugLifeCycleListener(IDebugLifeCycleListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("Parameter listener was null."); //$NON-NLS-1$
        }

        if (cycleListeners == null) {
            return;
        }

        cycleListeners.remove(listener);
    }
}
